package gnss_plugs

import (
	"dgo/goutils/utils"
	"encoding/base64"
	"errors"
	"fmt"
	"net"
	"strings"
	"sync/atomic"
	"time"
)

const (
	NTRIPCLT_STEP_READY_DATA      int = 3
	NTRIPCLT_STEP_SOURCETABLE     int = 4
	NTRIPCLT_STEP_WAIT_ForConnect int = 0
	NTRIPCLT_STEP_CONNECTED       int = 1
)

// recvBuf此处是复用的
type NtripClt struct {
	startflag     int32
	tcp           *AsyncTcpClt
	recvBuf       []byte
	firstrecv     string
	step          int
	resultcode    int
	processor     IGnssNtripCltProcessor
	postHeader    []byte
	UserAgent     string
	svrAddr       string
	reqMountPoint string
	reqUser       string
	reqPass       string
	reqNmea       string
	disInfo       string
	DailCb        func(network, addr string) (c net.Conn, err error)
}

func NewNtripClt() *NtripClt {
	rval := &NtripClt{
		recvBuf:   make([]byte, 512),
		startflag: 0,
		UserAgent: "DiocpNtripClt/1.0.1",
	}
	return rval
}

func (this *NtripClt) FirstRecv() string {
	return this.firstrecv
}

func (this *NtripClt) ResultCode() int {
	return this.resultcode
}

func (this *NtripClt) GetDisInfo() string {
	if len(this.disInfo) > 0 {
		return this.disInfo
	} else if this.tcp != nil {
		return this.tcp.GetDisInfo()
	} else {
		return ""
	}
}

func (this *NtripClt) String() string {
	str := fmt.Sprintf("%s:%s@%s/%s", this.reqUser, "****", this.svrAddr, this.reqMountPoint)
	return str
}

/***
 * 只能有HandleRecv纤程调用
 */
func (this *NtripClt) SetDebugStateString(str string) {
	this.tcp.state_debug_string = str
}

/*
**

	1: 建立连接
*/
func (this *NtripClt) Step() int {
	return this.step
}

func (this *NtripClt) ReqMountPoint() string {
	return this.reqMountPoint
}

func (this *NtripClt) RecvBytes() int64 {
	if this.tcp != nil {
		return this.tcp.recvBytes
	}
	return 0
}

func (this *NtripClt) SendBytes() int64 {
	if this.tcp != nil {
		return this.tcp.sendBytes
	}
	return 0
}

func (this *NtripClt) SvrAddr() string {
	return this.svrAddr
}

func (this *NtripClt) GetPostHeader() string {
	return string(this.postHeader)
}

func (this *NtripClt) checkTimeOutKickout() {
	for {
		if this.startflag == 0 {
			break
		}

		if this.tcp.connected && utils.SecondOf(this.tcp.lastRecvTime, time.Now()) > 30 {
			this.tcp.BreakConnect()
		}

		time.Sleep(time.Second * 10)
	}
}

func (this *NtripClt) SetNtripParams(svrAddr, reqM, reqUser, reqPass string) {
	this.svrAddr = svrAddr
	this.reqMountPoint = reqM
	this.reqUser = reqUser
	this.reqPass = reqPass

	str := "GET /" + this.reqMountPoint + " HTTP/1.0\r\n"
	str += fmt.Sprintf("User-Agent:NTRIP %s /1.0.1\r\n", this.UserAgent)
	if len(reqUser) > 0 {
		authstr := this.reqUser + ":" + this.reqPass
		authstr = base64.StdEncoding.EncodeToString([]byte(authstr))
		str += "Authorization: Basic " + authstr + "\r\n"
	}
	str += "\r\n"
	this.postHeader = []byte(str)
}

func (this *NtripClt) SetProcessor(processor IGnssNtripCltProcessor) {
	this.processor = processor
}

func (this *NtripClt) SendBuffer(buf []byte) (int, error) {
	if this.tcp != nil {
		return this.tcp.SendBuffer(buf)
	} else {
		return 0, nil
	}
}

func (this *NtripClt) Start() error {
	if atomic.CompareAndSwapInt32(&this.startflag, 0, 1) {
		if this.processor == nil {
			return errors.New("processor is nil!")
		}
		this.tcp = NewAsyncTcpClt(this)
		this.tcp.remoteAddress = this.svrAddr
		this.tcp.DailCb = this.DailCb
		this.tcp.Start()

		// 开启心跳
		utils.GoFunc2(this.checkTimeOutKickout)
	}

	return nil
}

func (this *NtripClt) Stop() {
	if atomic.CompareAndSwapInt32(&this.startflag, 1, 0) {
		this.tcp.Stop()
		this.tcp = nil
		if this.processor != nil {
			this.processor.OnNtripCltStop(this)
		}
		this.processor = nil
	}
}

func (this *NtripClt) OnTcpConnected(sender *AsyncTcpClt) {
	this.step = NTRIPCLT_STEP_CONNECTED
	this.tcp.SendBuffer(this.postHeader)
}

func (this *NtripClt) OnTcpRecvBuf(sender *AsyncTcpClt, buf []byte, l int) {
	if this.step == NTRIPCLT_STEP_CONNECTED {
		if l > 1024 {
			this.firstrecv = utils.Trim(string(buf[:1024]))
		} else {
			this.firstrecv = utils.Trim(string(buf[:l]))
		}

		s := strings.ToUpper(this.firstrecv)
		this.step = 2 // Step
		if strings.Index(s, "ICY 200 OK") != -1 {
			this.resultcode = 2
			this.step = NTRIPCLT_STEP_READY_DATA
			this.processor.OnNtripCltShakeRecv(this)
			return
		} else if strings.Index(s, "SOURCETABLE") != -1 {
			this.resultcode = 3
			this.processor.OnNtripCltShakeRecv(this)
			utils.GoFunc2(this.Stop) // 异步停止
		} else {
			this.resultcode = 4
			this.processor.OnNtripCltShakeRecv(this)
			utils.GoFunc2(this.Stop) // 异步停止
		}
	} else {
		this.processor.OnNtripCltData(this, buf, l)
	}
}

func (this *NtripClt) OnTcpMakeRecvBuf() []byte {
	return this.recvBuf
}

func (this *NtripClt) OnTcpDisconnected(sender *AsyncTcpClt) {
	this.step = 0
	this.processor.OnNtripCltDisconnected(this)
}

func (this *NtripClt) OnTcpConnectError(sender *AsyncTcpClt, err error) {
	this.processor.OnNtripCltConnectError(this, err)

}
